#include "gameroom.h"
#include "pool.h"

GameRoom::GameRoom(server* sver){
    roomID = generateID();
    s = sver;
    state = LOBBY;
    initLobby();
    //countdownTimer = new boost::asio::deadline_timer(s->get_io_service());
    iconlist = vector<bool>(8, true);

    roomPool[roomID] = this;
    readys = 0;
}
GameRoom::~GameRoom(){
    roomPool.erase(roomID);
}

const vector<Card> GameRoom::allcards = {Card(-1, TREASURE),
        Card(1,TREASURE), Card(2,TREASURE), Card(3,TREASURE), Card(4,TREASURE), Card(5,TREASURE), Card(7,TREASURE),
        Card(9,TREASURE), Card(11,TREASURE), Card(13,TREASURE), Card(14,TREASURE), Card(15,TREASURE), Card(17,TREASURE),
        Card(H1,HAZARD), Card(H2,HAZARD), Card(H3,HAZARD), Card(H4,HAZARD), Card(H5,HAZARD),
        Card(5, ARTIFACT), Card(7, ARTIFACT), Card(8, ARTIFACT), Card(10, ARTIFACT), Card(12, ARTIFACT) };

const vector<int> GameRoom::init_cards = {
        1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12,
        13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17
    };

const string GameRoom::characters = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";

void GameRoom::StartGame(){
    //cout << "to wait" << endl;
    //countdownTimer.async_wait(std::bind(TimerCallback, _1, s, conn, roomID));
    /*
    countdownTimer->expires_from_now(boost::posix_time::seconds(3));
    countdownTimer->async_wait([this](const boost::system::error_code& ec) {
        s->send(players[0]->getHdl(), "Countdown timer has ended!", websocketpp::frame::opcode::text);
        });

    */

}


random_device GameRoom::rd;
mt19937 GameRoom::mtrand(GameRoom::rd());

string GameRoom::generateID(){
    uniform_int_distribution<> dis(0, characters.size() - 1);
    string roomCode;
    for (int i = 0; i < 5; ++i) {
        int randomIndex = dis(mtrand);
        roomCode += characters[randomIndex];
    }
    
    if(roomPool.count(roomCode)) return generateID();
    
    return roomCode;
}


void GameRoom::initLobby(){
    lobbyinf["type"] = 0;
    lobbyinf["roomid"] = roomID;

    lobbyinf["players"]["num"] = 0;
    lobbyinf["players"]["plist"] = Json::Value(Json::arrayValue);
    plist = &lobbyinf["players"]["plist"];
}



ErrorType GameRoom::kick(Player* player){
    if(state != LOBBY) return ErrorType::WRONG_STATE_GAME;
    players.erase(player->getUid());
    if(players.empty()){
        delete this;
        return ErrorType::SUCCESS;
    }

    iconlist[player->icon] = true;

    lobbyinf["players"]["num"] = players.size();

    lobby_players.erase(player->getUid());

    for (int i = 0; i < plist->size(); i++) {
        if((*plist)[i]["uuid"].asString() == player->getUid()) plist->removeIndex(i, NULL);
    }

    if(readys == players.size() && readys >= 3){
        gameStart();
    }else{
        notifyAll(lobbyinf);
    }

    return ErrorType::SUCCESS;
}

bool GameRoom::notifyAll(const Json::Value& jmsg) const {
    Json::StreamWriterBuilder writer;
    writer["indentation"] = ""; // 不缩进，将JSON数据转换为紧凑的字符串
    // 将JSON值转换为字符串
    string jsonString = Json::writeString(writer, jmsg);

    bool e = true;
    for(auto& p:players){
        if(!notify(p.second->getHdl(),jsonString)){
            e = false;
        }
    }
    return e;
}

bool GameRoom::notifyAll(const string& msg) const {

    bool e = true;
    for(auto& p:players){
        if(!notify(p.second->getHdl(), msg)){
            e = false;
        }
    }
    return e;
}


const int GameRoom::artifact_cards[6] = {0, 18, 19, 20, 21, 22};

void GameRoom::initCards(){
    cards.assign(init_cards.begin(), init_cards.end());
}
void GameRoom::buildCards(){
    cards.push_back(artifact_cards[cur_round]);
    shuffle(cards.begin(), cards.end(), mtrand);
    cur_card = cards.begin();
}

bool GameRoom::notify(websocketpp::connection_hdl hdl, const string& msg) const {
    try {
        s->send(hdl, msg, websocketpp::frame::opcode::text);
    } catch (websocketpp::exception const & e) {
        std::cout << "Notify " <<hdl.lock() << " failed because: "
                << "(" << e.what() << ")" << std::endl;
        return false;
    }
    return true;
}

/*
{
    "type":"",
}
*/

Player::ICON GameRoom::popValidIcon(){
    int i = 0;
    while(!iconlist[i] && i<8) i++;
    iconlist[i] = false;
    return static_cast<Player::ICON>(i);
}

ErrorType GameRoom::addPlayer(Player* player){
    if(state != LOBBY) return ErrorType::WRONG_STATE_GAME;
    if(players.size() == 8) return ErrorType::FULL;
    player->icon = popValidIcon();
    string pid = player->getUid();
    players.emplace(pid, player);
    lobbyinf["players"]["num"] = players.size();
    plist->append(player->getSet());

    lobby_players[pid] = &((*plist)[plist->size() - 1]);

    notifyAll(lobbyinf);

    return ErrorType::SUCCESS;
}

/*
ErrorType GameRoom::notifyAll(GameRoom::MessageType type){
    switch(type){
    case ROOMSTATE:
        notifyAll(lobbyinf);
        return ErrorType::SUCCESS;
    case GAMESTATE:
        return ErrorType::WRONG_ARG;
    default:
        return ErrorType::WRONG_ARG;
    }
    
}
*/

ErrorType GameRoom::updatePlayer(string id){
    if(state == State::GAMING) return ErrorType::WRONG_STATE_GAME;

    Player* p = players[id];

    (*lobby_players[id]) = players[id]->getSet();

    /*
    for(int i=0; i<plist->size(); i++){
        if((*plist)[i]["uuid"].asString() == id){
            (*plist)[i] = p->getSet();
            cout<<lobby_players[id]<<endl;
            cout<<&((*plist)[i])<<endl;
            cout<<plist<<endl;
        }
    }
    */
    
    if(state == State::LOBBY) notifyAll(lobbyinf);

    return ErrorType::SUCCESS;
}

ErrorType GameRoom::updateReadyPlayer(string id){
    if(state == State::GAMING) return ErrorType::WRONG_STATE_GAME;
    readys++;
    updatePlayer(id);
    /*readys>3*/
    if(readys == players.size() && readys >= 3){
        gameStart();
    }
    return ErrorType::SUCCESS;
}

ErrorType GameRoom::updateUnReadyPlayer(string id){
    if(state == State::GAMING) return ErrorType::WRONG_STATE_GAME;
    readys--;
    return updatePlayer(id);
}

void GameRoom::buildGamePlist(){
    game_players.clear();

    gameinf["players"] = Json::Value(Json::arrayValue);
    Json::Value temp;
    temp["money"] = 0;
    temp["wealth"] = 0;
    temp["artifact"] = Json::Value(Json::arrayValue);
    temp["active"] = true;
    temp["stay"] = true;
    for (int i = 0; i < plist->size(); i++) {
        temp["uuid"] = (*plist)[i]["uuid"];
        temp["name"] = (*plist)[i]["name"];
        temp["icon"] = (*plist)[i]["icon"];
        gameinf["players"].append(temp);
        game_players.emplace(temp["uuid"].asString(), &(gameinf["players"][i]));
        //player_choice[temp["uuid"].asString()] = false;
        stay_list.insert(temp["uuid"].asString());
    }
    player_choice.clear();
}

void GameRoom::playerGo(string uid){
    cout<<player_choice.size()<<endl;

    if(player_choice.count(uid) == 0){
        player_choice.insert(uid);
        //(*game_players[uid])["stay"] = true;
        cout<<stay_list.size();
        if(player_choice.size() == stay_list.size()){
            next();
        }
    }
}

void GameRoom::playerBack(string uid){
    if(player_choice.count(uid) == 0){
        player_choice.insert(uid);
        (*game_players[uid])["stay"] = false;
        back_list.push_back(uid);
        if(player_choice.size() == stay_list.size()){
            next();
        }
    }
}

void GameRoom::next(){
    //清空已选择的玩家
    player_choice.clear();
    //判断是否有人独占了artifact
    if(back_list.size() == 1 && !artifacts.empty()){
        artifacts.clear();
    }
    //把返回列表中的人从stay列表中去除，更新stay列表
    for(auto& backid : back_list){
        stay_list.erase(backid);
    }
    //backlist每回合更新
    back_list.clear();
    //当前场上无人，全部返回，广播后进入下一轮
    if (stay_list.size() == 0) {
        notifyAll(gameinf);
        //NextRound
        nextRound();
        return;
    }
    //翻开当前卡牌，广播
    gameinf["cards"]["before"].append(gameinf["cards"]["next"]);
    gameinf["cards"]["next"] = *cur_card;
    notifyAll(gameinf);
    

    //如果出现了相同灾难，需要去掉这张牌，并进入下一轮
    if (*cur_card >= 13 && *cur_card <= 17) {
        if (hazard_record[*cur_card - 13]) {
            //NextRound
            cards.erase(cur_card);
            nextRound();
            return;
        }
        hazard_record[*cur_card - 13] = 1;
    }
    //如果出现了artifact需要记录
    if(*cur_card>=18){
        artifacts.push_back(*cur_card);
        cur_card = cards.erase(cur_card);
    }else{
        cur_card++;
    }
    
}

void GameRoom::initGame(){
    cur_round = 1;
    initCards();

    buildCards();
    
    gameinf["type"] = 2;
    gameinf["round"] = 1;
    //undistributed = 0;
    //gameinf["undistributed"] = 0;

    gameinf["cards"]["before"] = Json::Value(Json::arrayValue);
    //gameinf["cards"]["next"] = *cur_card;
    //cur_card++;


    artifacts.clear();
    back_list.clear();
    hazard_record[0] = 0;
    hazard_record[1] = 0;
    hazard_record[2] = 0;
    hazard_record[3] = 0;
    hazard_record[4] = 0;

    buildGamePlist();
}

void GameRoom::refreshGame(){
    /*
    for (auto& pair : game_players){
        (*pair.second)["money"] = 0;
        (*pair.second)["wealth"] = 0;
        (*pair.second)["artifact"].clear();
        (*pair.second)["active"] = true;
        (*pair.second)["stay"] = true;
        stay_list.insert(pair.first);
    }

    initGame();
    */
    state = State::ENDING;
    for(auto& p : players){
        p.second->unready();
    }
    state = State::LOBBY;
}

void GameRoom::gameStart(){
    state = GAMING;
    initGame();
    next();
}

void GameRoom::nextRound(){
    cout<<"Round Finished"<<endl;
    if(cur_round == 5){
        refreshGame();
        return;
    }
    gameinf["round"] = ++cur_round;

    hazard_record[0] = 0;
    hazard_record[1] = 0;
    hazard_record[2] = 0;
    hazard_record[3] = 0;
    hazard_record[4] = 0;

    if(!artifacts.empty()){
        cards.insert(cards.end(),artifacts.begin(),artifacts.end());
        artifacts.clear();
    }
    buildCards();
    for (auto& pair : game_players) {
        (*pair.second)["stay"] = true;
        //player_choice[temp["uuid"].asString()] = false;
        stay_list.insert(pair.first);
    }
    gameinf["cards"]["before"].clear();
    /*构造为newround做准备*/
    //不使用next的原因是，让先加载完的人能先进入下一局，使用next需要所有人同步。
    gameinf["cards"]["next"] = *cur_card;
    if (*cur_card >= 13 && *cur_card <= 17) hazard_record[*cur_card - 13] = 1;
    cur_card++;
}

void GameRoom::askForNewRound(websocketpp::connection_hdl hdl){
    Json::StreamWriterBuilder writer;
    writer["indentation"] = ""; // 不缩进，将JSON数据转换为紧凑的字符串
    // 将JSON值转换为字符串
    string jsonString = Json::writeString(writer, gameinf);
    notify(hdl, jsonString);
}

void GameRoom::askForLobby(websocketpp::connection_hdl hdl){
    Json::StreamWriterBuilder writer;
    writer["indentation"] = ""; // 不缩进，将JSON数据转换为紧凑的字符串
    // 将JSON值转换为字符串
    string jsonString = Json::writeString(writer, lobbyinf);
    notify(hdl, jsonString);
}